﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;

namespace WPFCharts
{
    /// <summary>
    /// Summary description for ChartLine.
    /// </summary>
    public class ChartLine
    {
        /// <summary>
        /// A list of points that define the shape of the primitive
        /// </summary>
        private List<Point> points;
        /// <summary>
        /// The minimum corner of the rectangle that holds all the points
        /// </summary>
        private Point minPoint;
        /// <summary>
        /// The maximum corner of the rectangle that holds all the points
        /// </summary>
        private Point maxPoint;
        /// <summary>
        /// The label for the primitive
        /// </summary>
        private string label;
        /// <summary>
        /// The last point added to the list
        /// </summary>
        private Point lastPoint;
        /// <summary>
        /// The line thickness
        /// </summary>
        private double lineThickness = 1;
        /// <summary>
        /// The paths for this primitive
        /// </summary>
        private PathFigure paths;
        /// <summary>
        /// The color used to draw the primitive
        /// </summary>
        public Color color;

        /// <summary>
        /// Constructor. Initializes class fields.
        /// </summary>
        /// <param name="primitiveType"></param>
        public ChartLine()
        {
            // Initialize min and max points

            // Set the brush to red
            color = Colors.Red;

            // Set a default label
            label = "Unlabled";

            points = new List<Point>();

            minPoint = new Point(0.0f, 0.0f);
            maxPoint = new Point(0.0f, 0.0f);
            paths = new PathFigure();

        }

        /// <summary>
        /// Copy constructor. Deep copies the ChartPrimitive passed in.
        /// </summary>
        /// <param name="chartPrimitive"></param>
        protected ChartLine(ChartLine chartLine)
            : this()
        {
            points.AddRange(chartLine.Points);
            minPoint = chartLine.MinPoint;
            maxPoint = chartLine.MaxPoint;
            color = chartLine.Color;
            label = chartLine.Label;
            lastPoint = chartLine.lastPoint;
            lineThickness = chartLine.LineThickness;
            paths = chartLine.paths.Clone();
        }

        /// <summary>
        /// Does a deep clone of the current ChartPrimitive
        /// </summary>
        /// <returns></returns>
        public ChartLine Clone()
        {
            return new ChartLine(this);
        }

        public void Clear()
        {
            points = new List<Point>();

            minPoint = new Point(0.0f, 0.0f);
            maxPoint = new Point(0.0f, 0.0f);
            paths = new PathFigure();
        }

        /// <summary>
        /// Adds a point to the primitive
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void AddPoint(double x, double y)
        {
            AddPoint(new Point(x, y));
        }

        /// <summary>
        /// Adds a point to the primitive
        /// </summary>
        /// <param name="point"></param>
        public void AddPoint(Point point)
        {
            // For normal lines, the points are added as is.
            if (points.Count > 0)
            {
                // If this isn't the first line being added,
                // then test to see if the new line increases
                // the area convered by all the lines
                UpdateMinMax(point);
                paths.Segments.Add(new LineSegment(point, true));
            }
            else
            {
                // First line being drawn so set the min/max points
                // to the points of the line being added
                minPoint = point;
                maxPoint = point;

                paths.StartPoint = point;
            }
            lastPoint = point;
            points.Add(point);
        }


        /// <summary>
        /// Inserts a point into the primitive
        /// </summary>
        /// <param name="point"></param>
        /// <param name="index"></param>
        public void InsertPoint(Point point, int index)
        {
            if (points.Count > 0)
            {
                if (index > 0)
                {
                    paths.Segments.Insert(index - 1, new LineSegment(point, true));
                }
                else
                {
                    paths.Segments.Insert(0, new LineSegment(paths.StartPoint, true));
                    paths.StartPoint = point;
                }
                UpdateMinMax(point);
            }
            else
            {
                AddPoint(point);
            }
        }

        /// <summary>
        /// Adds a bezier curve point where it gives a little plateau at the point
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void AddSmoothHorizontalBar(double x, double y)
        {
            AddSmoothHorizontalBar(new Point(x, y));
        }

        /// <summary>
        /// Adds a bezier curve point where it gives a little plateau at the point
        /// </summary>
        /// <param name="point"></param>
        public void AddSmoothHorizontalBar(Point point)
        {
            if (points.Count != 0)
            {
                double xDiff = (point.X - lastPoint.X) * .5;
                double yDiff = (point.Y - lastPoint.Y) * .3;

                Point controlPoint1 = new Point(lastPoint.X + xDiff, lastPoint.Y);
                Point controlPoint2 = new Point(point.X - xDiff, point.Y);
                UpdateMinMax(point);
                lastPoint = point;
                paths.Segments.Add(new BezierSegment(controlPoint1, controlPoint2, point, true));
            }
            points.Add(point);
        }

        /// <summary>
        /// Updates the Minimum and Maximum point with the point passed in
        /// </summary>
        /// <param name="point"></param>
        protected void UpdateMinMax(Point point)
        {
            minPoint.X = Math.Min(minPoint.X, point.X);
            minPoint.Y = Math.Min(minPoint.Y, point.Y);
            maxPoint.X = Math.Max(maxPoint.X, point.X);
            maxPoint.Y = Math.Max(maxPoint.Y, point.Y);
        }

        /// <summary>
        /// Gets the list of points added to this primitive
        /// </summary>
        public List<Point> Points
        {
            get { return points; }
        }

        /// <summary>
        /// Gets the path geometry of this primitive
        /// </summary>
        public PathGeometry PathGeometry
        { 
            get
            {
                PathGeometry geometry = new PathGeometry();
                geometry.Figures.Add(paths);
                return geometry;
            }
        }

        /// <summary>
        /// Gets the minium x,y values in the point collection
        /// </summary>
        public Point MinPoint
        {
            get { return minPoint; }
        }

        /// <summary>
        /// Gets the maximum x,y values in the point collection
        /// </summary>
        public Point MaxPoint
        {
            get { return maxPoint; }
        }

        /// <summary>
        /// Gets/Sets the color of the primitive
        /// </summary>
        public Color Color
        {
            get { return color; }
            set { color = value; }
        }

        /// <summary>
        /// Gets/Sets the line label
        /// </summary>
        public string Label
        {
            get { return label; }
            set { label = value; }
        }


        /// <summary>
        /// Gets/Sets the line thickness
        /// </summary>
        public double LineThickness
        {
            get { return lineThickness; }
            set { lineThickness = value; }
        }
    }
}
